<?php

class Router {

	protected $routes = null;
	
	function __construct() {
		$appRoutesFile = APP_PATH.'/config/routes.php';
		$sysRoutesFile = SYSTEM_PATH.'/config/routes.php';
		if( file_exists($appRoutesFile) ) {
			$appRoutes = include($appRoutesFile);
		}
		if( file_exists($sysRoutesFile) ) {
			$sysRoutes = include($sysRoutesFile);
		}
		$this->routes = array_merge( $sysRoutes, $appRoutes );
		
	}	
	
	function route() {
	
		// put segments into array form
		$segments = explode(  '/', urldecode( rtrim($_SERVER['PATH_INFO'], '/') ) );
		// trim the array up to and including the script name 
		// $controllerName = '';
		// while( $controllerName != basename($_SERVER['SCRIPT_NAME']) && count( $segments ) > 0 ) { 
		
			$controllerName = array_shift( $segments );
			
		// }
		// if there are no segments after script name, load the default controller
		// default controller is stored in system $this->routes['default']
		if( count( $segments ) === 0 ) $segments = array($this->routes['default']);
				
		// if there are, reroute first to custom routes before processing
		else $segments = $this->reroute( $segments );

		// build the segments one by one as a controller path
		// see if the controller exists
		$length = count( $segments );
		for( $offset = 1; $offset <= $length; $offset++ ) {	
			$controllerPath = implode('/', array_slice($segments, 0, $offset ) );
			if( file_exists( APP_PATH."/controllers/$controllerPath.php" ) ) {
				require(APP_PATH."/controllers/$controllerPath.php");	
				$controllerName = $segments[$offset-1];
				break;
			}
		}

		// we ran out of segments, but we found a controller with no arguments
		// load the controller, and its default method, the index
		if( $offset === $length ) {
			$controller = new $controllerName();
			if( is_callable( array($controller, 'index') ) ) call_user_func_array( array( $controller, 'index'), array_slice($segments, ++$offset ) ) ;
			// there's no index. 404.
			else $this->route404();
		}
		
		// if we did not run out of segments then we know that there is still 
		// the method and optionally the arguments,
		// also, make sure that the method is not a constructor nor destructor
		else if( $offset < $length ) {
			$controller = new $controllerName();
			$methodName = $segments[$offset];
			if( is_callable( array($controller, $methodName) ) 
				&& $methodName !== '__construct' 
				&& $methodName !== '__destruct' ) 
					call_user_func_array( array( $controller, $methodName), array_slice($segments, ++$offset ) ) ;
			// function can't be called. 404.
			else $this->route404(e);
		}
		
		// at this point we do not have any controllers present and we really don't know what to do now. Show a 404 then.
		else {
			$this->route404(d);
		}
		
	}



	function reroute($segments) {
		// is there routing to be done?
		if (count($this->routes) === 0) return $segments;
		$uri = implode('/', $segments);
		
		// Is there an exact match? 
		if (isset($this->routes[$uri]) )	return explode( '/', $this->routes[$uri] );		
				
		// begin matching
		foreach ($this->routes as $key => $val) {
			if (preg_match('#^'.$key.'$#', $uri)) {			
				// replace backrefs if there are any
				if (strpos($val, '$') !== FALSE AND strpos($key, '(' ) !== FALSE) 
					$val = preg_replace('#^'.$key.'$#', $val, $uri);
				return explode('/', $val);		
			}
		}
		
		// we got nothin'
		return $segments;
	}
	
	function route404() {
		header('HTTP/1.0 404 Not Found');
		try {
			new View('404');
		}
		// View wasn't found. We're sorta screwed and on our own here so let's just say something.
		catch( Exception $e ) {
			echo 'HTTP/1.0 404 Not Found';
		}
	}
	
}	
